#ifndef __TDP2_THREADING_HPP__
#define __TDP2_THREADING_HPP__

#include "../../rdx/src/rdx/rdx_threading.hpp"
#include "tdp2_linkedlist.hpp"

namespace TDP
{
	namespace Threading
	{
		using namespace RDX::Threading;

		class SimpleJob;

		// Multi-level dispatchers work as follows:
		// - The current thread attempts to 
		class MultilevelThreadDispatcher
		{
		public:
			struct DispatchableJob
			{
				DispatchableJob *nextJob;
				volatile AtomicInt finalize;				// Installing thread is done with the job and no work is left, free on next visitation
				volatile AtomicInt ownedThreads;			// Number of threads that may be accessing this
				volatile AtomicInt workExhausted;			// If >0, GetWork failed, so this thread is exhausted

				SimpleJob *job;

				void Digest(bool twiddle);	// Works on the job.  If no work could be performed, increments workExhausted and decrements ownedThreads
			};

		private:

			DispatchableJob *volatile _firstJob;
			DispatchableJob *volatile _lastJob;
			LightMutex _mutex;
			ISystemThread **_threads;
			volatile AtomicInt _active;
			ISystemThreadProc *_proc;
			int _numThreads;

		public:
			explicit MultilevelThreadDispatcher(int numThreads);
			~MultilevelThreadDispatcher();

			bool JobsPending();
			DispatchableJob *AcquireJob();
			void InstallJob(SimpleJob *job, bool twiddle);
			bool IsActive() const;
		};

		class SimpleThreadDispatcher
		{
		private:
			class RunSimpleJobProc : public ISystemThreadProc
			{
			private:
				volatile AtomicInt *_availableThreadCounter;
				SimpleJob *_job;

			public:
				inline RunSimpleJobProc(volatile AtomicInt *tc, SimpleJob *job)
				{
					_availableThreadCounter = tc;
					_job = job;
				}

				void Start() const;
			};

			AtomicInt _numAvailableThreads;
			int _numTotalThreads;

		public:
			SimpleThreadDispatcher();
			~SimpleThreadDispatcher();

			void DigestJob(SimpleJob *job, bool synchronous, bool suspendCurrent);
		};

		class SimpleJob
		{
		public:
			virtual void *GetWork() = 0;
			virtual void Work(void *ptr) = 0;
			virtual void Twiddle() = 0;			// Twiddle between jobs, or while waiting for other threads.  Only the dispatching thread ever twiddles.
		};
	};
};

#endif
